package com.hanter.android.radwidget;

import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.Drawable;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.util.Log;
import android.util.TypedValue;
import android.view.Gravity;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewGroup;
import android.widget.FrameLayout;
import android.widget.ImageView;
import android.widget.ImageView.ScaleType;
import android.widget.LinearLayout;
import android.widget.RelativeLayout;
import android.widget.TextView;

import androidx.annotation.NonNull;
import androidx.annotation.Nullable;

import com.etoonet.android.radwidget.R;
import com.hanter.android.radwidget.viewpager.PagerAdapter;
import com.hanter.android.radwidget.viewpager.ViewPager;

import java.util.ArrayList;
import java.util.List;

public class BannerView extends FrameLayout implements ViewPager.OnPageChangeListener {

    private static final String TAG = "Banner";

    public static final int DEFAULT_INTERVAL_TIME = 2000;
    public static final int BANNER_SCROLL_DURATION = 800;

    /**
     * indicator style
     */
    public static final int INDICATOR_NONE = 0;
    public static final int INDICATOR_CIRCLE = 2;
    public static final int INDICATOR_CIRCLE_TITLE = 3;
    public static final int INDICATOR_CIRCLE_TITLE_INSIDE = 4;
    public static final int INDICATOR_NUM = 5;
    public static final int INDICATOR_NUM_TITLE = 6;

    private int indicatorMargin;
    private int indicatorWidth;
    private int indicatorHeight;
    private int indicatorSelectedWidth;
    private int indicatorSelectedHeight;
    private int indicatorDrawable;
    private int indicatorGravity = Gravity.CENTER;
    private int indicatorStyle;
    @Nullable
    private Drawable placeholder;

    private int indicatorTitleTextSize;
    private int indicatorTitleTextColor;
    private int indicatorTitleHeight;
    private int indicatorTitleBackground;

    private int intervalTime;
    private int scrollDuration;
    private boolean autoPlay;
    private boolean scrollable;


    private int initItem;
    private int currentItem;
    private int lastPosition;
    private int scaleType;
    private List<String> titles;
    private List<Object> imageUrls;

    private Context context;
    private List<ImageView> indicatorImages;
    private BannerPager viewPager;
    private TextView bannerTitle;
    private TextView numIndicatorInside;
    private TextView numIndicator;
    private LinearLayout indicator;
    private LinearLayout indicatorInside;
    private LinearLayout titleView;

    private BannerLoader loader;
    private BannerPagerAdapter adapter;
    @Nullable
    private ViewPager.OnPageChangeListener onPageChangeListener;
    @Nullable
    private OnItemClickListener onItemClickListener;

    public interface BannerLoader {

        void displayImage(@NonNull Context context, @Nullable Object path,
                          @Nullable Drawable placeHolder, @NonNull ImageView imageView);

        ImageView createImageView(@NonNull ViewGroup container, int position);
    }

    public interface OnItemClickListener {
        void onItemClick(BannerView bannerView, int position);
    }

    public BannerView(Context context) {
        this(context, null);
    }

    public BannerView(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public BannerView(Context context, AttributeSet attrs, int defStyle) {
        super(context, attrs, defStyle);
        this.context = context;
        this.titles = new ArrayList<>();
        this.imageUrls = new ArrayList<>();
        this.indicatorImages = new ArrayList<>();
        initView(context, attrs);
    }

    private void initView(Context context, AttributeSet attrs) {
        handleTypedArray(context, attrs);
        View view = LayoutInflater.from(context).inflate(R.layout.view_banner, this, true);
        viewPager = view.findViewById(R.id.bannerViewPager);
        viewPager.setSettleDuration(scrollDuration);
        titleView = view.findViewById(R.id.titleView);
        indicator = view.findViewById(R.id.circleIndicator);
        indicatorInside = view.findViewById(R.id.indicatorInside);
        bannerTitle = view.findViewById(R.id.bannerTitle);
        numIndicator = view.findViewById(R.id.numIndicator);
        numIndicatorInside = view.findViewById(R.id.numIndicatorInside);
    }

    private void handleTypedArray(Context context, AttributeSet attrs) {
        if (attrs == null) {
            return;
        }
        TypedArray typedArray = context.obtainStyledAttributes(attrs, R.styleable.BannerView);

        DisplayMetrics dm = context.getResources().getDisplayMetrics();
        int indicatorSize = dm.widthPixels / 80;

        this.autoPlay = typedArray.getBoolean(R.styleable.BannerView_bv_autoPlay, false);
        this.intervalTime = typedArray.getInt(R.styleable.BannerView_bv_intervalTime, DEFAULT_INTERVAL_TIME);
        this.scrollable = typedArray.getBoolean(R.styleable.BannerView_bv_scrollable, true);
        this.scrollDuration = typedArray.getInt(R.styleable.BannerView_bv_scrollDuration, BANNER_SCROLL_DURATION);
        this.scaleType = typedArray.getInt(R.styleable.BannerView_bv_scaleType, ScaleType.CENTER_INSIDE.ordinal());

        indicatorTitleTextColor = typedArray.getColor(R.styleable.BannerView_bv_indicator_titleTextSize, Color.WHITE);
        indicatorTitleTextSize = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_titleTextSize,
                (int) (12 * dm.density + 0.5f));
        indicatorTitleBackground = typedArray.getColor(R.styleable.BannerView_bv_indicator_titleBackground, 0x44000000);
        indicatorTitleHeight = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_titleHeight,
                (int) (28 * dm.density + 0.5f));

        indicatorWidth = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_width, indicatorSize);
        indicatorHeight = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_height, indicatorSize);
        indicatorSelectedWidth = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_widthSelected, indicatorSize);
        indicatorSelectedHeight = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_heightSelected, indicatorSize);

        indicatorMargin = typedArray.getDimensionPixelSize(R.styleable.BannerView_bv_indicator_margin,
                (int) (4 * dm.density + 0.5f));
        indicatorDrawable = typedArray.getResourceId(R.styleable.BannerView_bv_indicator_drawable,
                R.drawable.ic_banner_indicator);
        indicatorStyle = typedArray.getInt(R.styleable.BannerView_bv_indicator_style, INDICATOR_CIRCLE);

        placeholder = typedArray.getDrawable(R.styleable.BannerView_bv_placeholder);

        typedArray.recycle();
    }

    public BannerPager getViewPager() {
        return viewPager;
    }

    public void setViewPager(BannerPager viewPager) {
        this.viewPager = viewPager;
    }

    public boolean isAutoPlay() {
        return autoPlay;
    }

    public BannerView setAutoPlay(boolean autoPlay) {
        this.autoPlay = autoPlay;
        return this;
    }

    public BannerView setBannerLoader(BannerLoader bannerLoader) {
        this.loader = bannerLoader;
        return this;
    }

    public BannerView setIntervalTime(int intervalTime) {
        this.intervalTime = intervalTime;
        return this;
    }

    public BannerView setIndicatorGravity(int gravity) {
        this.indicatorGravity = gravity;
        return this;
    }

    /**
     * Set the number of pages that should be retained to either side of the
     * current page in the view hierarchy in an idle state. Pages beyond this
     * limit will be recreated from the adapter when needed.
     *
     * @param limit How many pages will be kept offscreen in an idle state.
     * @return Banner
     */
    public BannerView setOffscreenPageLimit(int limit) {
        if (viewPager != null) {
            viewPager.setOffscreenPageLimit(limit);
        }
        return this;
    }

    /**
     * Set a {@link ViewPager.PageTransformer} that will be called for each attached page whenever
     * the scroll position is changed. This allows the application to apply custom property
     * transformations to each page, overriding the default sliding look and feel.
     *
     * @param reverseDrawingOrder true if the supplied PageTransformer requires page views
     *                            to be drawn from last to first instead of first to last.
     * @param transformer         PageTransformer that will modify each page's animation properties
     * @return Banner
     */
    public BannerView setPageTransformer(boolean reverseDrawingOrder, ViewPager.PageTransformer transformer) {
        viewPager.setPageTransformer(reverseDrawingOrder, transformer);
        return this;
    }

    public BannerView setBannerTitles(List<String> titles) {
        this.titles = titles;
        return this;
    }

    public BannerView setIndicatorStyle(int indicatorStyle) {
        this.indicatorStyle = indicatorStyle;
        return this;
    }

    public BannerView setViewPagerIsScroll(boolean isScroll) {
        this.scrollable = isScroll;
        return this;
    }

    public BannerView setImages(List<?> imageUrls) {
        this.imageUrls.addAll(imageUrls);
        return this;
    }

    public int getItemCount() {
        return imageUrls == null ? 0 : imageUrls.size();
    }

    @Override
    protected void onDetachedFromWindow() {
        stopAutoPlay();
        super.onDetachedFromWindow();
    }

    public void update(List<?> imageUrls, List<String> titles) {
        this.titles.clear();
        this.titles.addAll(titles);
        update(imageUrls);
    }

    public void update(List<?> imageUrls) {
        this.imageUrls.clear();
        this.indicatorImages.clear();
        this.imageUrls.addAll(imageUrls);
        start();
    }

    public void updateIndicatorStyle(int indicatorStyle) {
        indicator.setVisibility(GONE);
        numIndicator.setVisibility(GONE);
        numIndicatorInside.setVisibility(GONE);
        indicatorInside.setVisibility(GONE);
        bannerTitle.setVisibility(View.GONE);
        titleView.setVisibility(View.GONE);
        this.indicatorStyle = indicatorStyle;
        start();
    }

    public void start() {
        setIndicatorStyleUI();
        setImageList(imageUrls);
        setData();
    }

    private void setTitleStyleUI() {
        if (titles.size() != imageUrls.size()) {
            throw new RuntimeException("[Banner] --> The number of titles and images is different");
        }
        if (indicatorTitleBackground != -1) {
            titleView.setBackgroundColor(indicatorTitleBackground);
        }
        if (indicatorTitleHeight != -1) {
            titleView.setLayoutParams(new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, indicatorTitleHeight));
        }
        if (indicatorTitleTextColor != -1) {
            bannerTitle.setTextColor(indicatorTitleTextColor);
        }
        if (indicatorTitleTextSize != -1) {
            bannerTitle.setTextSize(TypedValue.COMPLEX_UNIT_PX, indicatorTitleTextSize);
        }
        if (titles != null && titles.size() > 0) {
            bannerTitle.setText(titles.get(currentItem));
            bannerTitle.setVisibility(View.VISIBLE);
            titleView.setVisibility(View.VISIBLE);
        }
    }

    private void setIndicatorStyleUI() {
        int visibility = getItemCount() > 1 ? View.VISIBLE : View.GONE;
        switch (indicatorStyle) {
            case INDICATOR_CIRCLE:
                indicator.setVisibility(visibility);
                break;
            case INDICATOR_NUM:
                numIndicator.setVisibility(visibility);
                break;
            case INDICATOR_NUM_TITLE:
                numIndicatorInside.setVisibility(visibility);
                setTitleStyleUI();
                break;
            case INDICATOR_CIRCLE_TITLE:
                indicator.setVisibility(visibility);
                setTitleStyleUI();
                break;
            case INDICATOR_CIRCLE_TITLE_INSIDE:
                indicatorInside.setVisibility(visibility);
                setTitleStyleUI();
                break;
        }
    }

    private void initImages() {
        if (indicatorStyle == INDICATOR_CIRCLE ||
                indicatorStyle == INDICATOR_CIRCLE_TITLE ||
                indicatorStyle == INDICATOR_CIRCLE_TITLE_INSIDE) {
            createIndicator();
        } else if (indicatorStyle == INDICATOR_NUM_TITLE) {
            numIndicatorInside.setText(getResources().getString(R.string.banner_indicator,
                    adapter.getRealPosition(currentItem),
                    getItemCount()));
        } else if (indicatorStyle == INDICATOR_NUM) {
            numIndicator.setText(getResources().getString(R.string.banner_indicator,
                    adapter.getRealPosition(currentItem),
                    getItemCount()));
        }
    }

    private void setImageList(List<?> imagesUrl) {
        if (imagesUrl == null || imagesUrl.size() <= 0) {
            Log.e(TAG, "The image data set is empty.");
            return;
        }
        initImages();
    }

    private void setScaleType(View imageView) {
        if (imageView instanceof ImageView) {
            ImageView view = ((ImageView) imageView);

            switch (scaleType) {
                case 0:
                    view.setScaleType(ScaleType.MATRIX);
                    break;
                case 1:
                    view.setScaleType(ScaleType.FIT_XY);
                    break;
                case 2:
                    view.setScaleType(ScaleType.FIT_START);
                    break;
                case 3:
                    view.setScaleType(ScaleType.FIT_CENTER);
                    break;
                case 4:
                    view.setScaleType(ScaleType.FIT_END);
                    break;
                case 5:
                    view.setScaleType(ScaleType.CENTER);
                    break;
                case 6:
                    view.setScaleType(ScaleType.CENTER_CROP);
                    break;
                case 7:
                    view.setScaleType(ScaleType.CENTER_INSIDE);
                    break;
            }

        }
    }

    private void createIndicator() {
        indicatorImages.clear();
        indicator.removeAllViews();
        indicatorInside.removeAllViews();
        int count = getItemCount();
        for (int i = 0; i < count; i++) {
            ImageView imageView = new ImageView(context);
            imageView.setScaleType(ScaleType.CENTER_CROP);
            LinearLayout.LayoutParams params;

            imageView.setImageResource(indicatorDrawable);
            if (i == 0) {
                params = new LinearLayout.LayoutParams(indicatorSelectedWidth, indicatorSelectedHeight);
                imageView.setSelected(true);
            } else {
                params = new LinearLayout.LayoutParams(indicatorWidth, indicatorHeight);
                imageView.setSelected(false);
            }
            params.leftMargin = indicatorMargin;
            params.rightMargin = indicatorMargin;
            indicatorImages.add(imageView);

            if (indicatorStyle == INDICATOR_CIRCLE ||
                    indicatorStyle == INDICATOR_CIRCLE_TITLE) {
                indicator.addView(imageView, params);
            } else if (indicatorStyle == INDICATOR_CIRCLE_TITLE_INSIDE) {
                indicatorInside.addView(imageView, params);
            }
        }
    }

    private void setData() {
        currentItem = initItem;

        if (adapter == null) {
            adapter = new BannerPagerAdapter();
            viewPager.addOnPageChangeListener(this);
            viewPager.setAdapter(adapter);
        } else {
            adapter.notifyDataSetChanged();
        }
        viewPager.setFocusable(true);
        viewPager.setCurrentItem(currentItem);

        indicator.setGravity(indicatorGravity);

        if (scrollable && getItemCount() > 1) {
            viewPager.setScrollable(true);
        } else {
            viewPager.setScrollable(false);
        }


        if (autoPlay)
            startAutoPlay();
    }

    public void startAutoPlay() {
        removeCallbacks(autoPlayRunnable);
        postDelayed(autoPlayRunnable, intervalTime);
    }

    public void stopAutoPlay() {
        removeCallbacks(autoPlayRunnable);
    }

    private final Runnable autoPlayRunnable = new Runnable() {
        @Override
        public void run() {
            if (getItemCount() > 1 && autoPlay) {
                currentItem = currentItem + 1;
                viewPager.setCurrentItem(currentItem);
                postDelayed(autoPlayRunnable, intervalTime);
            }
        }
    };

    @Override
    public boolean dispatchTouchEvent(MotionEvent ev) {
        if (autoPlay) {
            int action = ev.getAction();
            if (action == MotionEvent.ACTION_UP || action == MotionEvent.ACTION_CANCEL
                    || action == MotionEvent.ACTION_OUTSIDE) {
                startAutoPlay();
            } else if (action == MotionEvent.ACTION_DOWN) {
                stopAutoPlay();
            }
        }
        return super.dispatchTouchEvent(ev);
    }

    class BannerPagerAdapter extends PagerAdapter implements OnClickListener {

        @Override
        public boolean isLoop() {
            return true;
        }

        @Override
        public int getCount() {
            return imageUrls.size();
        }

        @Override
        public boolean isViewFromObject(@NonNull View view, @NonNull Object object) {
            return view == object;
        }

        @NonNull
        @Override
        public Object instantiateItem(@NonNull ViewGroup container, final int position) {
            int realPosition = getRealPosition(position);
            ImageView view = loader.createImageView(container, realPosition);
            setScaleType(view);
            view.setImageDrawable(placeholder);
            view.setTag(R.id.banner_position, realPosition);
            view.setOnClickListener(this);
            container.addView(view);
            loader.displayImage(container.getContext(), imageUrls.get(realPosition), placeholder, view);
            return view;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, @NonNull Object object) {
            container.removeView((View) object);
        }

        @Override
        public void onClick(View v) {
            if (onItemClickListener != null) {
                Integer position = (Integer) v.getTag(R.id.banner_position);
                onItemClickListener.onItemClick(BannerView.this, position);
            }
        }
    }

    @Override
    public void onPageScrollStateChanged(int state) {
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageScrollStateChanged(state);
        }
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageScrolled(adapter.getRealPosition(position), positionOffset, positionOffsetPixels);
        }
    }

    @Override
    public void onPageSelected(int position) {
        currentItem = position;
        if (onPageChangeListener != null) {
            onPageChangeListener.onPageSelected(adapter.getRealPosition(position));
        }

        if (indicatorStyle == INDICATOR_CIRCLE ||
                indicatorStyle == INDICATOR_CIRCLE_TITLE ||
                indicatorStyle == INDICATOR_CIRCLE_TITLE_INSIDE) {

            LinearLayout.LayoutParams selectedParams = new LinearLayout.LayoutParams(indicatorSelectedWidth, indicatorSelectedHeight);
            selectedParams.leftMargin = indicatorMargin;
            selectedParams.rightMargin = indicatorMargin;
            LinearLayout.LayoutParams unselectedParams = new LinearLayout.LayoutParams(indicatorWidth, indicatorHeight);
            unselectedParams.leftMargin = indicatorMargin;
            unselectedParams.rightMargin = indicatorMargin;

            int realLastPosition = adapter.getRealPosition(lastPosition);
            int realPosition = adapter.getRealPosition(position);

            indicatorImages.get(realLastPosition).setSelected(false);
            indicatorImages.get(realLastPosition).setLayoutParams(unselectedParams);

            indicatorImages.get(realPosition).setSelected(true);
            indicatorImages.get(realPosition).setLayoutParams(selectedParams);

            lastPosition = position;
        }

        int realPosition = adapter.getRealPosition(position);

        switch (indicatorStyle) {
            case INDICATOR_CIRCLE:
                break;

            case INDICATOR_NUM:
                numIndicator.setText(getResources().getString(R.string.banner_indicator,
                        realPosition,
                        getItemCount()));
                break;

            case INDICATOR_NUM_TITLE:
                numIndicatorInside.setText(getResources().getString(R.string.banner_indicator,
                        realPosition,
                        getItemCount()));
                bannerTitle.setText(titles.get(realPosition));
                break;

            case INDICATOR_CIRCLE_TITLE:
            case INDICATOR_CIRCLE_TITLE_INSIDE:
                bannerTitle.setText(titles.get(realPosition));
                break;
        }

    }

    public BannerView setOnItemClickListener(OnItemClickListener listener) {
        this.onItemClickListener = listener;
        return this;
    }

    public BannerView setInitItem(int index) {
        this.initItem = index;
        return this;
    }

    public void setOnPageChangeListener(@Nullable ViewPager.OnPageChangeListener onPageChangeListener) {
        this.onPageChangeListener = onPageChangeListener;
    }

    public void releaseBanner() {
        stopAutoPlay();
    }

    public static class BannerPager extends ViewPager {
        private boolean scrollable = true;

        public BannerPager(Context context) {
            super(context);
        }

        public BannerPager(Context context, AttributeSet attrs) {
            super(context, attrs);
        }

        @SuppressLint("ClickableViewAccessibility")
        @Override
        public boolean onTouchEvent(MotionEvent ev) {
            if (this.scrollable) {
                if (getCurrentItem() == 0 && getChildCount() == 0) {
                    return false;
                }
                return super.onTouchEvent(ev);
            } else {
                return false;
            }
        }

        @Override
        public boolean onInterceptTouchEvent(MotionEvent ev) {
            if (this.scrollable) {
                if (getCurrentItem() == 0 && getChildCount() == 0) {
                    return false;
                }
                return super.onInterceptTouchEvent(ev);
            } else {
                return false;
            }
        }

        public void setScrollable(boolean scrollable) {
            this.scrollable = scrollable;
        }
    }
}
